'''
This file is part of ModeliMark.

ModeliMark is free software: you can redistribute it and/or modify
it under the terms of the GNU Lesser General Public License as
published by the Free Software Foundation, either version 3 of
the License, or (at your option) any later version.

ModeliMark is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
GNU Lesser General Public License for more details.

You should have received a copy of the GNU Lesser General Public
License along with ModeliMark.
If not, see <http://www.gnu.org/licenses/>.

Copyright 2011 Jens Frenkel
'''

import wrapper
import numbers
import shlex, subprocess
import os
from time import *
from decimal import Decimal

class OMC(wrapper.BaseWrapper):
    '''
    classdocs
    '''

   
    def __init__(self, omcpath):
        '''
        Constructor
        
        :param omcpath: Path to the OMC executable.
        :type omcpath: String.         
        '''
        
        assert(isinstance(omcpath,str))
        self._omc = omcpath 
        
    def flatten(self, model, files):
        '''
        instructs the compiler to parse the Modelica 
        code and return the flat model
        
        :param model: Name of the Model to flatten.
        :type model: String.   
             
        :param files: all Files of the Model to flatten.
        :type files: list of String.             
        '''        
        assert(isinstance(model,str))
        assert(isinstance(files,list))
        
        # generate mos file
        mosfile = '%s.mos'%(model)
        f = open( mosfile, 'w' )
        for file in files:
            f.write('loadFile("%s");\n'%(file))
        f.write('instantiateModel(%s);\n'%(model))
        # Close File
        f.close()       
        
        #
        logfile = '%s_flatten_py.log'%(model) 
        res, dt = self._run(logfile,[mosfile])
        if (res == 0):
            res = self._readResult_flatten(logfile,len(files))
        else:
            res = False
               
        return res, dt        
      
    def translate(self, model, files):
        '''
        flattens the model and turns it into a state which can be simulated
        
        :param model: Name of the Model to flatten.
        :type model: String.   
             
        :param files: all Files of the Model to flatten.
        :type files: list of String. 
        '''         
        assert(isinstance(model,str))
        assert(isinstance(files,list))      
       
        # generate mos file
        mosfile = '%s.mos'%(model)
        f = open( mosfile, 'w' )
        for file in files:
            f.write('loadFile("%s");\n'%(file))
        f.write('translateModel(%s);\n'%(model))
        # Close File
        f.close()       
        
        #
        logfile = '%s_translate_py.log'%(model) 
        res, dt = self._run(logfile,[mosfile])
        if (res == 0):
            res = self._readResult_translate(logfile,len(files))
        else:
            res = False
               
        return res, dt             
        
    def simulate(self, model, files, startTime, outputInterval, stopTime):
        '''
        flattens, translates and simulates the model using the standard solver
        and a predefined output interval and stop time
        
        :param model: Name of the Model to flatten.
        :type model: String.   
             
        :param files: all Files of the Model to flatten.
        :type files: list of String. 
        
        :param startTime: Start Time of the Simulation
        :type startTime: numbers.Real.
                
        :param outputInterval: Intervall for Output of Results.
        :type outputInterval: numbers.Real. 
        
        :param stopTime: Stop Time of the Simulation
        :type stopTime: numbers.Real. 
        '''         
        assert(isinstance(model,str))
        assert(isinstance(files,list))
        assert(isinstance(startTime,numbers.Real))
        assert(isinstance(outputInterval,numbers.Real))
        assert(isinstance(stopTime,numbers.Real))        
        
        # generate mos file
        mosfile = '%s.mos'%(model)
        f = open( mosfile, 'w' )
        for file in files:
            f.write('loadFile("%s");\n'%(file))
        f.write('simulate(%s,startTime=%s,stopTime=%s,outputInterval=%s);\n'%(model,str(startTime),str(stopTime),str(outputInterval)))
        # Close File
        f.close()       
        
        #
        logfile = '%s_simulate_py.log'%(model) 
        res, dt = self._run(logfile,[mosfile])
        if (res == 0):
            res = self._readResult_simulate(logfile,len(files))
        else:
            res = False
               
        return res, dt           
        
    def _run(self,logfilename,args):
        
        assert(isinstance(logfilename,str))
        
        ar = list()
        ar.append(self._omc)
        for a in args:
            ar.append(a)
            
        print 'starte OMC'
        f = open( logfilename, 'w' )
        t1 = clock()
        p = subprocess.Popen(args=ar,stdout=f)
        run = True
        while run:
            res = p.poll()
            if (res is not None):
                run = False    
        t2 = clock()
        dt = t2 - t1
        f.close() 
        return res, dt 
        
    def _readResult_flatten(self,logfilename,nFiles):
        
        assert(isinstance(logfilename,str))
        assert(isinstance(nFiles,int))
        
        f = open( logfilename, 'r' )
        for n in xrange(nFiles):
            ln = f.readline()
            if (ln != 'true\n'): return False
        res = list()
        firstline = f.readline()
        res.append(firstline[1:len(firstline)])
        for line in f:
            res.append(line)
        if (len(res)>1):
            res.pop(len(res)-1)

        f.close() 
        
        return res
        
    def _readResult_translate(self,logfilename,nFiles):
        
        assert(isinstance(logfilename,str))
        assert(isinstance(nFiles,int))
        
        f = open( logfilename, 'r' )
        for n in xrange(nFiles):
            ln = f.readline()
            if (ln != 'true\n'): return False
        res = True


        f.close() 
        
        return res        
        
    def _readResult_simulate(self,logfilename,nFiles):
        
        assert(isinstance(logfilename,str))
        assert(isinstance(nFiles,int))
        
        f = open( logfilename, 'r' )
        for n in xrange(nFiles):
            ln = f.readline()
            if (ln != 'true\n'): return False
        ln = f.readline()
        ln = f.readline()
        ln = f.readline()
        ln = f.readline()
        tfi = ln.find('failed')
        if (not(tfi == -1)): return False
        ln = f.readline()
        tfi = ln.find('=')
        if (tfi == -1): return False
        lf = ln[tfi+1:ln.rfind(',')]
        timeFrontend = Decimal(lf)
        ln = f.readline()
        tfi = ln.find('=')
        if (tfi == -1): return False
        lf = ln[tfi+1:ln.rfind(',')]
        timeBackend = Decimal(lf)
        ln = f.readline()
        tfi = ln.find('=')
        if (tfi == -1): return False
        lf = ln[tfi+1:ln.rfind(',')]
        timeSimCode = Decimal(lf)
        ln = f.readline()
        tfi = ln.find('=')
        if (tfi == -1): return False
        lf = ln[tfi+1:ln.rfind(',')]
        timeTemplates = Decimal(lf)
        ln = f.readline()
        tfi = ln.find('=')
        if (tfi == -1): return False
        lf = ln[tfi+1:ln.rfind(',')]
        timeCompile = Decimal(lf)
        ln = f.readline()
        tfi = ln.find('=')
        if (tfi == -1): return False
        lf = ln[tfi+1:ln.rfind(',')]
        timeSimulation = Decimal(lf)
        ln = f.readline()
        tfi = ln.find('=')
        if (tfi == -1): return False
        lf = ln[tfi+1:len(ln)]
        timeTotal = Decimal(lf)
        f.close() 
        
        return [timeFrontend,timeBackend,timeSimCode,timeTemplates,timeCompile,timeSimulation,timeTotal]
          
        
        
        